{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# TwoPortOnePath, EnhancedResponse, and FakeFlip" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Intro" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example demonstrates a macgyver-ish shortcut you can take if you are measuring a device that is ** reciprocal** and **symmetric** on a switch-less three-receiver system. For more information about error correction this type of architecture, see [Calibration With Three Receivers](Calibration With Three Receivers.ipynb).\n", "\n", "In general, full error correction of a 2-port network on a switchless three-receiver architecture requires each DUT to measured in two orientations. However, if the DUT is known to be reciprocal ($S_{21}=S_{12}$) and symmetric ($S_{11}=S_{22}$), then measurements in both orientations produce the same response, and therefore are unnecessary.\n", "\n", "The following worked example compares the corrected response of a 10dB attenuator at WR-12 as corrected using full error correction and pseudo-full error correction using:\n", "\n", "1. Full Correction\n", "2. Pseudo-Full Correction (FakeFlip)\n", "3. Partial (EnhancedResponse)\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "\n", "Image('three_receiver_cal/pics/macgyver.jpg', width='50%')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These measurements where taken on a Agilent PNAX with a set of VDI WR-12 TXRX-RX Frequency Extender heads. The measurements of the calibration standards and DUT's were downloaded from the VNA by saving touchstone files of the raw s-parameter data to disk. \n", "\n", "In the code that follows a TwoPortOnePath calibration is created from corresponding measured and ideal responses of the calibration standards. The measured networks are read from disk, while their corresponding ideal responses are generated using scikit-rf. More information about using scikit-rf to do offline calibrations can be found [here](../../tutorials/Calibration.ipynb). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "import skrf as rf\n", "\n", "%matplotlib inline\n", "\n", "from skrf import mil\n", "from skrf import two_port_reflect as tpr\n", "from skrf.calibration import TwoPortOnePath\n", "from skrf.media import RectangularWaveguide\n", "\n", "rf.stylely()\n", "\n", "raw = rf.read_all_networks('three_receiver_cal/data/')\n", "\n", "\n", "\n", "# pull frequency information from measurements\n", "frequency = raw['short'].frequency\n", "\n", "# the media object\n", "wg = RectangularWaveguide(frequency=frequency, a=120*mil, z0_override=50)\n", "\n", "# list of 'ideal' responses of the calibration standards\n", "ideals = [wg.short(nports=2),\n", " tpr(wg.delay_short( 90,'deg'), wg.match()),\n", " wg.match(nports=2),\n", " wg.thru()]\n", "\n", "# corresponding measurements to the 'ideals'\n", "measured = [raw['short'],\n", " raw['quarter wave delay short'],\n", " raw['load'],\n", " raw['thru']]\n", "\n", "# the Calibration object\n", "cal = TwoPortOnePath(measured = measured, ideals = ideals )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Correction Options" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the calibration created above, we compare the corrected response of WR-12 10dB attenuator using **Full**, **Pseudo-Full**, and **Partial** Correction. Each correction algorithm is described below. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Image('three_receiver_cal/pics/symmetric DUT.jpg', width='75%')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Full Correction (TwoPortOnePath)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Full correction on this type of architecture has been called *TwoPortOnePath*. In `scikit-rf` using this correction algorithm requires the device to be measured in both orientations, **forward** and **reverse**, and passing them both to the `apply_cal()` function as a `tuple`. Neglecting the connector uncertainty, this type of correction is identical to full two-port **SOLT** calibration." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Pseudo-full Correction ( FakeFlip)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we assume the DUT is **reciprocal** and **symmetric**, then measuring the device in both orientations will produce the same result. Therefore, the reverse orientation measurement may be replaced by a copy of the forward orientation measurement. We refer to this technique as the *Fake Flip*. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "**Warning**: \n", "Be sure that you understand the assumptions of reciprocity and symmetry before using this macgyver technique, incorrect usage can lead to nonsense results.\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Partial Correction (EnhancedResponse)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you pass a single measurement to the `apply_cal()` function, then the calibration will employ partial correction. This type of correction is known as `EnhancedResponse`. While the *Fake Flip* technique assumes the device is reciprocal and symmetric, the `EnhancedResponse` algorithm *implicitly* assumes that the port 2 of the device is perfectly matched. The accuracy of the corrected result produced with either of these algorithms depends on accuracy of the assumptions. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comparison" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dutf = raw['attenuator (forward)']\n", "dutr = raw['attenuator (reverse)']\n", "\n", "\n", "# note the correction algorithm is different depending on what is passed to\n", "# apply_cal\n", "corrected_full = cal.apply_cal((dutf, dutr))\n", "corrected_fakeflip = cal.apply_cal((dutf,dutf))\n", "corrected_partial = cal.apply_cal(dutf)\n", "\n", "\n", "\n", "f, ax = plt.subplots(2,2, figsize=(8,8))\n", "\n", "for m in [0,1]:\n", " for n in [0,1]:\n", " ax_ = ax[m,n]\n", " ax_.set_title('$S_{%i%i}$'%(m+1,n+1))\n", " corrected_full.plot_s_db(m,n, label='Full Correction',ax=ax_ )\n", " corrected_fakeflip.plot_s_db(m,n, label='Pseudo-full Correction', ax=ax_)\n", " if n==0:\n", " corrected_partial.plot_s_db(m,n, label='Partial Correction', ax=ax_)\n", "\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 1 }